﻿#include "AudioPlayer.h"
#include <QDebug>
#include <QCoreApplication>
#include <QTimerEvent>
#include <QElapsedTimer>
#include <QDateTime>
#include <QTimer>
#include "AudioFilter.h"

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <QAudioSource>
#include <QMediaDevices>
#else
#include <QAudioDeviceInfo>
#endif

extern "C"
{
#include <libavutil/channel_layout.h>
}

AudioPlayer::AudioPlayer(QObject *parent)
    : ISynchronizer(parent)
{
    m_td = QThread::create(&AudioPlayer::run, this);
    m_td->start();
    m_initSema.acquire();
}

AudioPlayer::~AudioPlayer()
{
    m_td->requestInterruption();
    m_td->wait();
    delete m_td;
}

void AudioPlayer::start()
{
    if(QThread::currentThread() != m_td) {
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::start, this), Qt::BlockingQueuedConnection);
        return;
    }

    stop();

    m_expired = 0;
    audioDevice = audioSink->start();
    m_presentSerialMutex.lock();
    m_presentSerial = 0;
    m_presentSerialMutex.unlock();
}

void AudioPlayer::stop()
{
    if(QThread::currentThread() != m_td) {
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::stop, this), Qt::BlockingQueuedConnection);
        return;
    }

    audioSink->stop();
    audioDevice = nullptr;
}

double AudioPlayer::clock()
{
    return m_expired.load() / 1000000.0;
}

bool AudioPlayer::isPausing()
{
    return m_isPaused;
}

void AudioPlayer::pause()
{
    auto impl = [this]() {
        audioSink->suspend();
        m_isPaused = true;
    };

    if(QThread::currentThread() != audioSink->thread())
        audioSink->metaObject()->invokeMethod(audioSink, impl, Qt::BlockingQueuedConnection);
    else
        impl();
}

void AudioPlayer::resume()
{
    auto impl = [this]() {
        audioSink->resume();
        m_isPaused = false;
    };

    if(QThread::currentThread() != audioSink->thread())
        audioSink->metaObject()->invokeMethod(audioSink, impl, Qt::BlockingQueuedConnection);
    else
        impl();
}

void AudioPlayer::seek(double timepoint)
{
    if(m_decoder && m_decoder->isRunning()) {
        m_decoder->seek(timepoint);
    }

    std::lock_guard<std::mutex> locker(m_presentSerialMutex);
    m_presentSerial = int64_t(timepoint * 1000000.0);
}

void AudioPlayer::setPresentSpeed(double speed)
{
    m_presentSerial = int64_t(clock() * 1000000.0);
    m_presentSpeed = int32_t(speed * 1000000.0);
}

DecodecSerial AudioPlayer::presentSerial()
{
    std::lock_guard<std::mutex> locker(m_presentSerialMutex);
    return m_presentSerial;
}

void AudioPlayer::run()
{
    QAudioFormat format;
    format.setSampleRate(44100);
    format.setChannelCount(2);

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)

    format.setSampleFormat(QAudioFormat::SampleFormat::Int16);

    auto preformat = QMediaDevices::defaultAudioOutput().preferredFormat();
    qDebug() << "Default audio output device: \r\n"
             << "\t Sample rate: " << preformat.sampleRate() << "\r\n"
             << "\t   Channels : " << preformat.channelCount() << "\r\n"
             << "\t Sample type: " << preformat.sampleFormat() << "\r\n";
             
    audioSink = new QAudioSink(format);
#else
    auto preformat = QAudioDeviceInfo::defaultOutputDevice().preferredFormat();

    qDebug() << "Default audio output device: \r\n"
             << "\t Sample rate: " << preformat.sampleRate() << "\r\n"
             << "\t   Channels : " << preformat.channelCount() << "\r\n"
             << "\t Sample type: " << preformat.sampleType() << "\r\n"
             << "\t Sample size: " << preformat.sampleSize() << "\r\n";

    format.setSampleType(QAudioFormat::SampleType::SignedInt);
    format.setSampleSize(16);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);

    // 使用默认音频输出设备
    audioSink = new QAudioOutput(format);
#endif

    m_initSema.release();

    qsizetype dataSize = 0;

    qint64 currProcessedUSecs = 0;
    int32_t currPresentSpeed = 0;

    AudioFilter atempoFilter;
    DecodecSerial serial;

    while (!m_td->isInterruptionRequested()) {
        qApp->processEvents(QEventLoop::AllEvents, 30);

        if(!audioDevice || !m_decoder || !m_decoder->isRunning()) {
            QThread::yieldCurrentThread();
            continue;
        }

        auto currSerial = presentSerial();
        if(serial != currSerial) {
            serial = currSerial;

            if(m_curFrame)
                av_frame_free(&m_curFrame);

            if(currPresentSpeed != m_presentSpeed) {
                currPresentSpeed = m_presentSpeed;
                atempoFilter.cleanup();
                QString filterDesc = QString("atempo=%1").arg(currPresentSpeed / 1000000.0);
                atempoFilter.initFilterGraph(AVRational{1, format.sampleRate()}, format.sampleRate(), AV_SAMPLE_FMT_S16, AV_CH_LAYOUT_STEREO,
                                             format.sampleRate(), AV_SAMPLE_FMT_S16, AV_CH_LAYOUT_STEREO,
                                             filterDesc.toUtf8().data());
            }

            currProcessedUSecs = audioSink->processedUSecs();
        }

        if(!m_curFrame) {

            // 从队列中获取一帧
            auto frame = m_decoder->nextAudioFrame();
            if(!frame) {
                QThread::yieldCurrentThread();
                continue;
            }

            // 应用速度
            m_curFrame = atempoFilter.filter(frame);
            if(!m_curFrame){
                av_frame_free(&frame);
                QThread::msleep(15);
                continue;
            }

            av_frame_free(&frame);

            m_curDataPos = 0;
            dataSize = av_samples_get_buffer_size(nullptr, m_curFrame->channels,
            m_curFrame->nb_samples,
            static_cast<AVSampleFormat>(m_curFrame->format), 1);

            m_expired = uint64_t((audioSink->processedUSecs() - currProcessedUSecs) * (m_presentSpeed / 1000000.0)) + serial.timepoint;
            emit clockChanged();
        }

        auto bytesFree = audioSink->bytesFree();
        if (bytesFree == 0 || audioSink->state() == QAudio::SuspendedState) {
            QThread::msleep(15);
            continue;
        }

        qsizetype s = std::min((qsizetype)bytesFree, dataSize - m_curDataPos);
        audioDevice->write((const char*)(m_curFrame->data[0]) + m_curDataPos, s);

        m_curDataPos += s;

        if (m_curDataPos == dataSize) {
            av_frame_free(&m_curFrame);
        }
    }

    audioSink->stop();
    delete audioSink;
    qInfo() << "The audio player is exciting";
}

double AudioPlayer::volume() const
{
    if(!audioSink)
        return 1.0;

    if(QThread::currentThread() != audioSink->thread()) {
        double ret;
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::volume, this), Qt::BlockingQueuedConnection, &ret);
        return ret;
    }

    return audioSink->volume();
}

void AudioPlayer::setVolume(double newVolume)
{
    if(!m_td)
        return;

    if(QThread::currentThread() != audioSink->thread()) {
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::setVolume, this, newVolume), Qt::BlockingQueuedConnection);
        return;
    }

    audioSink->setVolume(newVolume);
}

Decoder *AudioPlayer::decoder()
{
    if(QThread::currentThread() != m_td) {
        Decoder* ret = nullptr;
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::decoder, this), Qt::BlockingQueuedConnection, &ret);
        return ret;
    }

    return m_decoder;
}

void AudioPlayer::setDecoder(Decoder *newDecoder)
{
    if(QThread::currentThread() != m_td) {
        QMetaObject::invokeMethod(audioSink, std::bind(&AudioPlayer::setDecoder, this, newDecoder), Qt::BlockingQueuedConnection);
        return;
    }

    if (m_decoder == newDecoder)
        return;

    m_decoder = newDecoder;
    emit decoderChanged();
}
